/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/


#include "stdafx.h"
#include <pgl/core/PGLPie.h>
#include <pgl/core/PGLSolidBrush.h>

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

PGL_IMPLEMENT_DYNCREATE(CPGLPie, CPGLObject);

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

//! Save settings
void CPGLPie::SerializeXML(CPGLArchive& ar)
{
	static const TCHAR szTag[] = _T("CPGLPie");
	CString str;
	CPGLPieDataList::const_iterator it;
	CPGLPieDataPtr pData;

	markup::CMarkupArchive& xml = *((markup::CMarkupArchive*)ar.GetMarkup());
	if(ar.IsStoring())
	{
		xml.AddChildElem(szTag);
		xml.AddChildAttrib(_T("Name"),GetName());
		xml.IntoElem();

			/////////////////////////////////////////////////////////
			// Adding base class
			CPGLObject::SerializeXML(ar);

			xml.AddChildElemEx(_T("AutoColoring"), m_bAutoColoring);
			xml.AddChildElem(_T("ColorMap"),CPGLUtility::ColorMap2String(m_eColorMap));
			xml.AddChildElemEx(_T("BkgAlpha"), m_dAlpha);
			xml.AddChildElemEx(_T("Radius"), m_dnRadius);
			xml.AddChildElemEx(_T("InsideRadius"), m_dnInRadius);
			xml.AddChildElemEx(_T("Phase"),m_dPhase);
			xml.AddChildElemEx(_T("ExplodedRadius"), m_dpExplodedRadius);
			xml.AddChildElem(_T("Center"));
			xml.IntoElem();
				xml.AddChildElemEx(_T("X"),m_pnCenter.x);
				xml.AddChildElemEx(_T("Y"),m_pnCenter.y);
			xml.OutOfElem();

			xml.AddChildElem(_T("ValueText"));
			xml.IntoElem();
				m_tValues.SerializeXML(ar);
			xml.OutOfElem();

			xml.AddChildElem(_T("Data"));
			xml.IntoElem();
			for (it = m_lDatas.begin(); it != m_lDatas.end(); it++)
			{
				(*it)->SerializeXML(ar);
			}
			xml.OutOfElem();


		xml.OutOfElem();
	}
	else
	{
		if  (!xml.FindChildElem(szTag))
				return;
		Clear();

		CString strPos;
		strPos.Format(_T("%u"),(UINT)this);

		xml.IntoElem();
			/////////////////////////////////////////////////////////
			// Adding base class
			CPGLObject::SerializeXML(ar);

			xml.FindGetChildData(_T("AutoColoring"), m_bAutoColoring);
			xml.FindGetChildData(_T("ColorMap"),str);
			m_eColorMap=CPGLUtility::String2ColorMap(str);
			xml.FindGetChildData(_T("BkgAlpha"), m_dAlpha);
			m_dAlpha = __min(1, __max( 0, m_dAlpha));
			xml.FindGetChildData(_T("Radius"), m_dnRadius);
			xml.FindGetChildData(_T("InsideRadius"), m_dnInRadius);
			xml.FindGetChildData(_T("Phase"),m_dPhase);
			xml.FindGetChildData(_T("ExplodedRadius"), m_dpExplodedRadius);

			xml.ResetChildPos();
			if (xml.FindChildElem(_T("Center")))
			{
				xml.IntoElem();
					xml.FindGetChildData(_T("X"),m_pnCenter.x);
					xml.FindGetChildData(_T("Y"),m_pnCenter.y);
				xml.OutOfElem();
			}

			xml.ResetChildPos();
			if (xml.FindChildElem(_T("ValueText")))
			{
				xml.IntoElem();
					m_tValues.SerializeXML(ar);
				xml.OutOfElem();
			}

			xml.ResetChildPos();
			if(xml.FindChildElem(_T("Data")))
			{
				xml.IntoElem();
					xml.SavePos(strPos);
					while (xml.FindChildElem())
					{
						if(xml.GetChildTagName() != _T("CPGLPieData"))
							continue;

						pData=CPGLPieDataPtr(new CPGLPieData);
						xml.RestorePos(strPos);
							pData->SerializeXML(ar);
						xml.SavePos(strPos);
						PushFront(pData);
					}
				xml.OutOfElem();
			}
		xml.OutOfElem();
	}
}


#ifdef _DEBUG
void CPGLPie::Dump( CDumpContext& dc ) const
{
    // call base class function first
    CPGLObject::Dump( dc );

    // now do the stuff for our specific class
	dc << _T("CPGLPie ID ") << GetID() << "\n";
}
void CPGLPie::AssertValid() const
{
    // call inherited AssertValid first
    CPGLObject::AssertValid();

    // check members...
} 
#endif

CPGLPie::CPGLPie()
: CPGLObject(), m_bAutoColoring(true), m_dPhase(0), m_dnRadius(0.5), m_dnInRadius(0),
m_dpExplodedRadius(10), m_pnCenter(0.5,0.5), m_eColorMap(CPGLUtility::ColorMapJet),
m_dAlpha(0.5)
{
	m_tValues.SetString(_T("value"));
	m_tValues.SetName(_T("Value text type"));
	m_tValues.SetAlign(gfxinterface::TextAlignmentCenter);
}

CPGLPie& CPGLPie::operator = (const CPGLPie& l)
{
	// prevent self-assignement
	if (&l != this)
	{
		// invoke CPGLObject copy assignement operator
		this->CPGLObject::operator =(l);

		// clean memory
		Clear();

		// copy
		m_bAutoColoring = l.m_bAutoColoring;
		m_eColorMap = l.m_eColorMap;
		m_dAlpha = l.m_dAlpha;
		m_dnRadius= l.m_dnRadius;
		m_dnInRadius= l.m_dnInRadius;
		m_dpExplodedRadius= l.m_dpExplodedRadius;
		m_pnCenter = l.m_pnCenter;
		CPGLPieDataPtr pPieData;

		CPGLPieDataList::const_iterator io;
		for (io = l.m_lDatas.begin(); io != l.m_lDatas.end(); ++io)
		{
			if (!(*io))
				continue;
			pPieData = boost::shared_polymorphic_cast<CPGLPieData, CPGLNode>( (*io)->MakeCopy() );
			m_lDatas.push_back(pPieData);
		}
	}
	return *this;
}

void CPGLPie::PlotGfx(gfxinterface::CGfxInterface& gfx)
{
	using namespace std;
	using namespace gfxinterface;

	double twopi = 2 * PGL_PI;
	double angle;
	double arc;
	CPGLSolidBrush brush;
	CPGLColor col;
	int i;
	static CString str;

	if (!IsVisible())
		return;


	// calling base class plot
	CPGLObject::PlotGfx(gfx);


	// no data
	if (m_lDatas.size()==0)
		return;

	double sum = GetDataSum();

	// sum is null
	if (sum <= std::numeric_limits<double>::denorm_min())
		return;

	// ok let's go...
	CPGLPieDataPtr pData;
	CPGLPieDataList::const_iterator id;
	angle = m_dPhase;

	for (id = m_lDatas.begin(), i=0; id != m_lDatas.end(); id++, i++)
	{
		pData = *id;
		if (!pData)
			continue;

		arc = pData->GetValue()/sum*twopi;

		if (m_bAutoColoring)
		{
			CPGLUtility::ColorMap(m_eColorMap, i/((double)m_lDatas.size()-1),col);
			col.SetAlpha((float)m_dAlpha);
			pData->SetSolidFillColor(col);
			pData->SetColor(0,0,0);
		}

		pData->GetFillBrush()->MakeCurrentFill(gfx);
		pData->MakeFrontColorCurrent(gfx);

		if (pData->IsExploded())
		{
			if (m_dnInRadius)
			{
				gfx.DrawDonnut(m_pnCenter.x, m_pnCenter.y, m_dnInRadius, m_dnRadius,  angle*180/PGL_PI, arc*180/PGL_PI, m_dpExplodedRadius,
						ArcTypeFill | ArcTypeContour | ArcTypeExploded);
			}
			else
			{
				gfx.DrawArc(m_pnCenter.x, m_pnCenter.y, m_dnRadius, angle*180/PGL_PI, arc*180/PGL_PI, m_dpExplodedRadius,
						ArcTypeFill | ArcTypeContour | ArcTypeExploded);
			}
		}
		else
		{
			if (m_dnInRadius)
			{
				gfx.DrawDonnut(m_pnCenter.x, m_pnCenter.y,m_dnInRadius,m_dnRadius,  angle*180/PGL_PI, arc*180/PGL_PI, 0,
						ArcTypeFill | ArcTypeContour);
			}
			else
			{
				gfx.DrawArc(m_pnCenter.x, m_pnCenter.y, m_dnRadius, angle*180/PGL_PI, arc*180/PGL_PI, 0,
						ArcTypeFill | ArcTypeContour);
			}
		}

		angle+=arc;
	}


	if (m_tValues.IsVisible())
	{
		vector<SPointD> points(m_lDatas.size());
		vector<_tstring> strings(m_lDatas.size());
		m_tValues.SetString(_T(""));
		m_tValues.PlotGfx(gfx);
		angle = m_dPhase;
		const REAL radius = (REAL)__min(m_dnRadius*gfx.GetBBClipWidth(),m_dnRadius*gfx.GetBBClipHeight());
		for (id = m_lDatas.begin(), i=0; id != m_lDatas.end(); id++, i++)
		{
			pData = *id;
			if (!pData)
				continue;

			arc = pData->GetValue()/sum*twopi;
			
			points[i].x= gfx.NormClipBBoxToWorldx( m_pnCenter.x) + gfx.BBToWorldAbsx(radius*2/3.0*cos(angle+arc/2));
			points[i].y= gfx.NormClipBBoxToWorldy(1- m_pnCenter.y)-gfx.GetYDirectionUp()*gfx.BBToWorldAbsy(radius*2/3.0*sin(angle+arc/2));
			str.Format(_T("%2.2g%%"), pData->GetValue()/sum*100);
			strings[i]=str;
			angle+=arc;
		}

		gfx.DrawTextStrip(points, strings, m_tValues.GetAlign(), m_tValues.GetAngle());
	}

}		

void CPGLPie::GetUnionExtent(gfxinterface::CGfxInterface& gfx, CPGLRectD& rExtent)
{
	CPGLObject::GetUnionExtent(gfx, rExtent);

	rExtent.xValid=false;
	rExtent.yValid=false;
}

void CPGLPie::UpdateExtent(gfxinterface::CGfxInterface& gfx)
{
	// Calling base class function
	CPGLObject::UpdateExtent(gfx);

	CPGLRectD rExtent;
	rExtent.left=m_pnCenter.x-m_dnRadius;
	rExtent.right= m_pnCenter.x+m_dnRadius;
	rExtent.bottom=m_pnCenter.y-m_dnRadius;
	rExtent.top= m_pnCenter.y+m_dnRadius;

	SetExtent(rExtent);
}


void CPGLPie::Clear()
{
	m_lDatas.clear();
}

double CPGLPie::GetDataSum() const
{
	CPGLPieDataList::const_iterator id;
	double sum=0;
	for (id=m_lDatas.begin() ; id!= m_lDatas.end(); id++)
	{
		ASSERT(*id);
		sum+=(*id)->GetValue();
	}
	return sum;
}

void CPGLPie::PushLinesTop(CPGLLegendPtr pLegend)
{
	using namespace std;

	if (! pLegend)
		return;


#pragma warning(push)
#include <yvals.h>              // warning numbers get enabled in yvals.h 
#pragma warning(disable: 4786)  
	CPGLPieDataList::reverse_iterator il;
	for (il = m_lDatas.rbegin(); il != m_lDatas.rend(); ++il)
	{
		pLegend->PushTop( *il);
	}
#pragma warning(pop)
}
